﻿using Blm.RabbitMQ.MessageQueue;
using Blm.Utils.Extensions;
using Blm.Utils.Utility;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using VisionCloud.Domain.Common;
using VisionCloud.Domain.DtoModel.Ms;
using VisionCloud.Domain.Repository.Interface.Ms;
using VisionCloud.Domain.UnitOfWork;
using static VisionCloud.Core.Model.Ms.TaskPoints;

namespace VisionCloud.Service.Ms
{
    /// <summary>
    /// 点云数据服务
    /// </summary>
    public class MsTaskDotCloudService
    {
        public IUnitOfWork UnitOfWork { get; set; }

        public IMsTaskRepository MsTaskRepository { get; set; }

        public IMsTaskDotCloudRepository MsTaskDotCloudRepository { get; set; }

        public IMsTaskDotCloudBatchRepository MsTaskDotCloudBatchRepository { get; set; }

        public IRabbitMQProducer RabbitMQProducer { get; set; }

        public IMsTaskPointsRepository MsTaskPointsRepository { get; set; }

        /// <summary>
        /// 实测云端计算接口
        /// </summary>
        private const string MeasuringApi = "api/Algorithm/Measuring";

        /// <summary>
        /// 实测云端汇总报告接口
        /// </summary>
        private const string MeasuringReportApi = "api/Algorithm/MeasuringReport";

        private readonly string CloudMeasureServer;

        private readonly ILogger<MsTaskDotCloudService> logger;

        private readonly bool useCloudMeasureApi;

        public MsTaskDotCloudService(IConfiguration cfg, ILogger<MsTaskDotCloudService> logger)
        {
            this.logger = logger;
            CloudMeasureServer = cfg["CloudMeasureServer"];
            useCloudMeasureApi = cfg.GetValue<bool>("UseCloudMeasureApi");
        }

        /// <summary>
        /// 批量添加或更新点云数据文件
        /// PS：只采模式的点云实时发起云端计算
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddOrUpdateDotCloud(DotCloudUploadRequestModel model)
        {
            var list = await MsTaskDotCloudRepository.AddorUpdateDotCloud(model);

            #region 只采模式点云发起云端测量

            var ids = list.Select(p => p.TaskId).ToArray();
            var taskList = await MsTaskRepository.WhereAsync(p => ids.Contains(p.TaskId));
            List<MeasuringAlgorithmDto> measurings = new List<MeasuringAlgorithmDto>();
            foreach (var item in list)
            {
                var t = taskList.FirstOrDefault(p => p.TaskId == item.TaskId);
                if (t?.TaskExecutionType != "2")
                    continue;

                var flag = await MsTaskPointsRepository.UpdatePointStatus(item.TaskId, new int[] { item.Site.ToInt() }, PointStatus.采集完成);
                if (!flag)
                {
                    logger.LogError($"更新只采模式的子站点状态失败：TaskId ={item.TaskId}，PointIndex = {item.Site}");
                    continue;
                }

                measurings.Add(new MeasuringAlgorithmDto
                {
                    TaskId = item.TaskId,
                    ProjectId = item.ProjectId,
                    FileId = item.FileId,
                    PointIndex = item.Site.ToInt32(),
                    PcdFile = item.DotCloudFilePath
                });
            }
            _ = await ApplyCloudMeasure(measurings);

            #endregion

            return new ApiResult { Code = list.Count > 0 ? AjaxCode.Success : AjaxCode.Fail };
        }

        /// <summary>
        /// 请求云端实测实量计算
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public async ValueTask<(int, int)> ApplyCloudMeasure(List<MeasuringAlgorithmDto> list)
        {
            return useCloudMeasureApi ? await ApplyCloudMeasureByHttp(list) : await ApplyCloudMeasureByMQ(list);
        }

        /// <summary>
        /// 请求云端实测实量计算（直接rabbitmq发送消息）
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private async ValueTask<(int, int)> ApplyCloudMeasureByMQ(List<MeasuringAlgorithmDto> list)
        {
            if (list.IsEmpty())
                return (0, 0);

            int successCount = 0;
            Parallel.ForEach(list, new ParallelOptions { MaxDegreeOfParallelism = 10 }, async (item) =>
            {
                await RabbitMQProducer.SendAsync(item);
                successCount++;
            });

            return (successCount, list.Count - successCount);
        }

        /// <summary>
        /// 请求云端实测实量计算（通过网关API发送消息）
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private async ValueTask<(int, int)> ApplyCloudMeasureByHttp(List<MeasuringAlgorithmDto> list)
        {
            if (list.IsEmpty())
                return (0, 0);

            int successCount = 0;
            var url = CloudMeasureServer + MeasuringApi;
            Dictionary<string, string> headers = new Dictionary<string, string>
            {
                { "ClientId", "VisionCloud.Web" }
            };
            Parallel.ForEach(list, new ParallelOptions { MaxDegreeOfParallelism = 10 }, async (item) =>
            {
                var data = item.ToJsonStr();
                var response = await HttpUtility.PostAsync(url, data, headers: headers);
                if (response == null || response.StatusCode != HttpStatusCode.OK)
                {
                    logger.LogError($"请求云端实测算法失败：{data}");
                    return;
                }

                var result = await response.Content.ReadAsStringAsync();
                var apiResult = result.ToObject<ApiResult>();
                if (apiResult == null || apiResult.Code != AjaxCode.Success)
                {
                    logger.LogError($"请求云端实测算法失败：{data}");
                    return;
                }

                successCount++;
            });

            return (successCount, list.Count - successCount);
        }

        /// <summary>
        /// 记录只采模式点云上传的批次
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public async Task<ApiResult> AddBatch(DotCloudUploadBatchDto model)
        {
            var flag = await MsTaskDotCloudBatchRepository.AddDotCloudBatchAsync(model);

            return new ApiResult { Code = flag ? AjaxCode.Success : AjaxCode.Fail };
        }

        /// <summary>
        /// 查询待计算的批次点云，发起云端计算请求
        /// </summary>
        /// <returns></returns>
        public async Task<ApiResult> BatchApplyCloudMeasure()
        {
            var apiResult = new ApiResult { Code = AjaxCode.Success };
            var batchList = (await MsTaskDotCloudBatchRepository.WhereAsync(p => p.Status == 1 && !p.IsDeleted)).OrderBy(p => p.UpdateDate).ToList();
            if (batchList.IsEmpty())
                return apiResult;

            int successCount = 0;
            int failedCount = 0;
            #region 只采模式点云发起云端测量

            foreach (var bt in batchList)
            {
                var dotCloudList = (await MsTaskDotCloudRepository.WhereAsync(p => p.TaskId == bt.TaskId && bt.PointIndexs.Contains(p.Site) && !p.IsDeleted)).ToList();
                List<MeasuringAlgorithmDto> measurings = new List<MeasuringAlgorithmDto>();
                foreach (var dt in dotCloudList)
                {
                    measurings.Add(new MeasuringAlgorithmDto
                    {
                        TaskId = dt.TaskId,
                        ProjectId = dt.ProjectId,
                        FileId = dt.FileId,
                        PointIndex = dt.Site.ToInt32(),
                        PcdFile = dt.DotCloudFilePath
                    });
                    if (dt.ComputeStatus != 1)
                    {
                        dt.ComputeStatus = 1;
                        dt.LastUpdatedDate = DateTime.Now;
                        MsTaskDotCloudRepository.Update(dt, false);
                    }
                }
                bt.Status = 2;
                bt.UpdateDate = DateTime.Now;
                MsTaskDotCloudBatchRepository.Update(bt, false);
                var count = await UnitOfWork.SaveChangesAsync();
                if (count > 0)
                {
                    var (sCount, fCount) = await ApplyCloudMeasure(measurings);
                    successCount += sCount;
                    failedCount += fCount;
                }
                else
                {
                    logger.LogError($"只采模式请求云端计算失败：{bt.ToJsonStr()}");
                }
            }

            #endregion

            apiResult.Message = $"成功：{successCount}，失败：{failedCount}";

            return apiResult;
        }

        /// <summary>
        /// 查询子站点已完成的批次，发起汇总报告请求
        /// </summary>
        /// <returns></returns>
        public async ValueTask<ApiResult> ApplySummaryReport()
        {
            var apiResult = new ApiResult { Code = AjaxCode.Success };
            var batchList = (await MsTaskDotCloudBatchRepository.WhereAsync(p => p.Status == 2 && !p.IsDeleted && p.CurrTimes < p.MaxTimes)).OrderBy(p => p.UpdateDate).ToList();
            if (batchList.IsEmpty())
                return apiResult;

            List<MeasuringReportDto> reportDtos = new List<MeasuringReportDto>();
            foreach (var bt in batchList)
            {
                var dotCloudList = (await MsTaskDotCloudRepository.WhereAsync(p => p.TaskId == bt.TaskId && bt.PointIndexs.Contains(p.Site))).ToList();
                if (dotCloudList != null && dotCloudList.Any(p => p.ComputeStatus == 4) && !dotCloudList.Any(p => p.ComputeStatus == 1 || p.ComputeStatus == 2 || p.ComputeStatus == 99))
                {
                    var taskInfo = await MsTaskRepository.FirstOrDefaultAsync(p => p.TaskId == bt.TaskId && !p.IsDeleted);
                    MeasuringReportDto reportDto = new MeasuringReportDto
                    {
                        BatchId = bt.BatchId,
                        TaskId = bt.TaskId,
                        IsSummaryReport = true,
                        ProjectId = taskInfo.ProjectId
                    };
                    reportDtos.Add(reportDto);
                }
            }

            return await ApplySummaryReport(reportDtos);
        }

        /// <summary>
        /// 发起汇总报告请求
        /// </summary>
        /// <param name="reportDtos"></param>
        /// <returns></returns>
        public async ValueTask<ApiResult> ApplySummaryReport(List<MeasuringReportDto> reportDtos)
        {
            return useCloudMeasureApi ? await ApplySummaryReportByHttp(reportDtos) : await ApplySummaryReportByMQ(reportDtos);
        }

        /// <summary>
        /// 发起汇总报告请求
        /// </summary>
        /// <returns></returns>
        public async ValueTask<ApiResult> ApplySummaryReportByMQ(List<MeasuringReportDto> reportDtos)
        {
            var apiResult = new ApiResult { Code = AjaxCode.Success };
            if (reportDtos.IsEmpty())
                return apiResult;

            foreach (var item in reportDtos)
            {
                await MsTaskDotCloudBatchRepository.UpdateCurrTimes(item.BatchId.Value);
                await RabbitMQProducer.SendAsync(item);
            }

            return apiResult;
        }

        /// <summary>
        /// 发起汇总报告请求
        /// </summary>
        /// <returns></returns>
        public async ValueTask<ApiResult> ApplySummaryReportByHttp(List<MeasuringReportDto> reportDtos)
        {
            var apiResult = new ApiResult { Code = AjaxCode.Success };
            if (reportDtos.IsEmpty())
                return apiResult;

            var url = CloudMeasureServer + MeasuringReportApi;
            Dictionary<string, string> headers = new Dictionary<string, string>
            {
                { "ClientId", "VisionCloud.Web" }
            };

            foreach (var item in reportDtos)
            {
                var data = item.ToJsonStr();

                await MsTaskDotCloudBatchRepository.UpdateCurrTimes(item.BatchId.Value);

                var response = await HttpUtility.PostAsync(url, data, headers: headers);
                if (response == null || response.StatusCode != HttpStatusCode.OK)
                {
                    logger.LogError($"请求云端实测汇总报告失败：{data}");
                }

                var result = await response.Content.ReadAsStringAsync();
                var ar = result.ToObject<ApiResult>();
                if (ar == null || ar.Code != AjaxCode.Success)
                {
                    logger.LogError($"请求云端实测汇总报告失败：{data}");
                }
            }

            return apiResult;
        }
    }
}